﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using NetworkServices;

namespace SearchRS
{
    public abstract class SearchWithResultsPage : SearchRS
    {
        protected string m_searchString;
        protected string m_searchOrder;
        private string m_subsLink;

        private readonly List<WebRequest> m_pendingRequests = new List<WebRequest>();      
        private ManualResetEvent m_finishEvent;

        private class OpenWarezBBResultCtx
        {
            public int ResultId { get; set;  }
            public ManualResetEvent FinishEvent { get; set; }
            public string Title { get; set; }
            public string Link { get; set; }
        }

        protected abstract INotifications NotificationsClient { get; set; }

        protected abstract string SiteName { get; }
        protected abstract string BaseURL { get; }
        protected abstract string SearchURL { get; }
        protected abstract string LoginURL { get; }

        protected abstract string Sid { get; set; }
        protected abstract string SidCookieParam { get; }
        protected abstract string SearchCookieParam { get; set; }
        protected abstract string PageCookieParam { get; }

        protected abstract string ResultTitleStartString { get; }
        protected abstract string ResultTitleFinishString { get; }
        protected abstract string TitleStartPosString { get; }

        protected abstract bool IsPost { get; }
        protected abstract string PostOrGetParams(bool searchAppsAndGamesAndEbooksToo);

        protected ManualResetEvent loginSuccessfullEvent { get; set; }

        public SearchWithResultsPage(SearchRsParams searchRsParams) :
            base(searchRsParams)
        {
            loginSuccessfullEvent = new ManualResetEvent(false);
        }

        public void Search(string searchString, bool searchAppsAndGamesAndEbooksToo, string searchOrder, string subsLink, bool shouldEncodeToASCII)
        {
            m_searchString = searchString;
            m_searchOrder = searchOrder;
            m_finishEvent = new ManualResetEvent(false);
            m_subsLink = subsLink;

            m_pendingRequests.Clear();

            NotificationsClient.SetStatusMessageAndProgressAmount("Waiting for login to finish for " + SiteName, 1);

            if (!loginSuccessfullEvent.WaitOne(20000))
            {
                NotificationsClient.SetStatusMessageAndProgressAmount("Unable to login to " + SiteName + ". Skip searching in it", 100);
                AsyncWebRequest.PendingRequests--;
                return;
            }

            NotificationsClient.SetStatusMessageAndProgressAmount("Searching '" + searchString + "' in " + SiteName, 11);

            AsyncWebRequest request = new AsyncWebRequest();
            WebRequest req = request.SendWebBBRequest(SearchURL,
                             SearchCookieParam,
                             PostOrGetParams(searchAppsAndGamesAndEbooksToo),
                             10,
                             Sid,
                             IsPost,
                             IsPost ? LoginURL : SearchURL,
                             ShouldStopEvent,
                             0,
                             "",
                             shouldEncodeToASCII,
                             SendPhpBBRequestCB, searchAppsAndGamesAndEbooksToo);

            m_pendingRequests.Add(req);
            m_finishEvent.WaitOne();
            m_pendingRequests.Clear();
        }

        public void SetStop()
        {
            ShouldStopEvent.Set();
            while (m_pendingRequests.Count > 0)
            {
                WebRequest req = m_pendingRequests[0];
                m_pendingRequests.RemoveAt(0);
                if (req != null)
                    req.Abort();
            }
        }

        private void SendPhpBBRequestCB(string buffer, HttpWebResponse responseObj, string searchCookieParam, object userState)
        {
            if (string.IsNullOrEmpty(buffer))
            {
                m_finishEvent.Set();
                return;
            }

            if (buffer.Contains("Sorry "))
            {
                m_finishEvent.Set();
                return;
/*                bool searchAppsAndGamesAndEbooksToo = (bool)userState;
                SendWebBBRequest(SearchURL,
                 SearchCookieParam,
                 PostOrGetParams(searchAppsAndGamesAndEbooksToo),
                 10,
                 SendPhpBBRequestCB, searchAppsAndGamesAndEbooksToo);
                return;*/
            }

            SearchCookieParam = searchCookieParam;

            FindLinksInWarezbbSearchResults(buffer);

            m_finishEvent.Set();
        }

        private void FindLinksInWarezbbSearchResults(string buffer)
        {
            // Find links in search result            
            NotificationsClient.SetStatusMessageAndProgressAmount("Enumerating " + SiteName + " search results", 20);
            int titleStartPos = 0;

            List<ManualResetEvent> events = new List<ManualResetEvent>();
            while (true)
            {
                if (ShouldStop())
                    break;

                int bufPos = buffer.IndexOf("<form method=\"post\"");
                if (bufPos > 0)
                    buffer = buffer.Substring(bufPos);
                titleStartPos = buffer.IndexOf(ResultTitleStartString, titleStartPos + 1);
                if (titleStartPos > 0)
                {
                    int titleEndPos = buffer.IndexOf(ResultTitleFinishString, titleStartPos);
                    string title = buffer.Substring(titleStartPos, titleEndPos - titleStartPos);
                    //                    int rsPos = Math.Max(title.ToLower().IndexOf("rs.com"), title.ToLower().IndexOf("[rs"));
                    //                    if (rsPos >= 0)

                    int pos = buffer.LastIndexOf("<img src=\"./images/icons/misc", titleStartPos);
                    if (pos > -1)
                    {
                        string img = buffer.Substring(pos, titleStartPos - pos);
                        if (img.Contains("ms.gif"))
                            continue;
                    }
                    if (!title.Contains("[NL]") && !title.Contains("[MU]") && !title.Contains("[MS]") && !title.Contains("[FS]") && !title.Contains("[HF]") && !title.Contains("[HF/FS]") && !title.Contains("[FSrv]") && !title.Contains("[HF/FS/FS]"))
                    {
                        buffer = buffer.Replace("</a>\t", "</a>").Replace("</a>\t", "</a>").Replace("</a>\t", "</a>").Replace(
                                "</a>\t", "</a>").Replace("</a>\t", "</a>");
                        buffer =
                            buffer.Replace("</a> ", "</a>").Replace("</a> ", "</a>").Replace("</a> ", "</a>").Replace(
                                "</a> ", "</a>").Replace("</a> ", "</a>").Replace("</a> ", "</a>");

                        const string c_ahrefTag = "<a href=\"";
                        const string c_ahrefTagAlt = "</a><a href=\"";
                        string ahrefTag = c_ahrefTag;
                        if (title.IndexOf(ahrefTag) == -1)
                        {
                            if (buffer.IndexOf(c_ahrefTagAlt, titleStartPos) != -1)
                            {
                                ahrefTag = c_ahrefTagAlt;
                            }
                                                        
                        }
                        int linkStartPos = buffer.IndexOf(ahrefTag, titleStartPos);

                        int linkEndPos1 = buffer.IndexOf("&amp;", linkStartPos);
                        int linkEndPos2 = buffer.IndexOf("&amp;hilit", linkStartPos + ahrefTag.Length + 1);
                        int linkEndPos = Math.Max(linkEndPos1, linkEndPos2);
                        string link = buffer.Substring(linkStartPos + ahrefTag.Length, linkEndPos - linkStartPos - ahrefTag.Length);
                        if (link.StartsWith("."))
                            link = link.Substring(1);
                        if (!link.StartsWith("/"))
                            link = "/" + link;
                        link = link.Replace("&amp;", "&");
                        pos = link.IndexOf("\"");
                        if (pos > -1)
                            link = link.Substring(0, pos);

                        int startTitlePos = title.IndexOf(TitleStartPosString) + TitleStartPosString.Length;
                        int endTitlePos = title.IndexOf("<", startTitlePos);
                        if (endTitlePos == -1)
                            endTitlePos = title.Length;
                        title = title.Substring(startTitlePos, endTitlePos - startTitlePos);
                        if (title.Contains(">"))
                            title = title.Substring(title.LastIndexOf(">") + 1);
                        if (ShouldPickTheLink(title, 0, title.Length, m_searchString, m_searchOrder))
                        {
                            link = BaseURL + link;
                            int resultId = AddResult(title, link, null, m_subsLink, "", ResultState.Unverified);

                            ManualResetEvent finishEvent = new ManualResetEvent(false);
                            events.Add(finishEvent);

                            OpenWarezBBResultPageAndAddLinks(link, resultId, finishEvent, title);
                        }
                    }
                }
                else
                {
                    // No results
                    break;
                }
            }

            ManualResetEvent[] waitHandles = new ManualResetEvent[events.Count];
            for (int i = 0; i < events.Count; i++)
                waitHandles[i] = events[i];
            if (events.Count > 0)
                WaitHandle.WaitAll(waitHandles);

            return;
        }

        private void OpenWarezBBResultPageAndAddLinks(string link, int resultId, ManualResetEvent finishEvent, string title)
        {
            OpenWarezBBResultCtx ctx = new OpenWarezBBResultCtx {ResultId = resultId, FinishEvent = finishEvent, Title = title, Link = link};

            AsyncWebRequest request = new AsyncWebRequest();
            WebRequest req = request.SendWebBBRequest(link,
                                     PageCookieParam,
                                     "",
                                     10,
                                     Sid,
                                     IsPost,
                                     IsPost ? LoginURL : SearchURL,
                                     ShouldStopEvent,
                                     0,
                                     "",
                                     false,              
                                     OpenPhpBBResultPageAndAddLinksCB, ctx);
            m_pendingRequests.Add(req);
        }

        private void OpenPhpBBResultPageAndAddLinksCB(string buffer, HttpWebResponse responseObj, string searchCookieParam, object userState)
        {
            string pwd = "";
            OpenWarezBBResultCtx ctx = userState as OpenWarezBBResultCtx;

            int r=0;
            if (ctx.Link.Equals("http://www.warez-bb.org/viewtopic.php?t=7468537"))
            {
                r=1;
            }

            List<string> links = null;
            do
            {
                if (ShouldStop())
                    break;

                if (string.IsNullOrEmpty(buffer))
                    break;

                SearchCookieParam = searchCookieParam;

                try
                {
                    buffer = buffer.Replace("&#58;", ":");
                    int pos = buffer.IndexOf("http://rapidshare.com/users");
                    if (pos >= 0)
                    {
                        string url = buffer.Substring(pos, buffer.IndexOf("</td>", pos) - pos);
                        WebResponse resp = NetworkServices.NetworkServices.SendGetRequest(url, false, "");
                        StreamReader sr = new StreamReader(resp.GetResponseStream());
                        string s = sr.ReadToEnd();

                        Regex re = new Regex("a href=\"(http://www.filesonic.com/[^\"]+)");
                        MatchCollection matches = re.Matches(s);
                        foreach (Match m in matches)
                        {
                            string link = m.Groups[1].Value;
                            pos = link.LastIndexOf("/");
                            if (pos < 0)
                                continue;
                            string cleanLink = link.Substring(pos + 1);

                            pos = cleanLink.IndexOf(".part");
                            if (pos < 0)
                                break;

                            cleanLink = Utils.Utils.CleanName(cleanLink.Substring(0, pos));
                            if (Utils.Utils.AllWordsIncluded(ctx.Title, cleanLink))
                            {
                                if (links == null)
                                    links = new List<string>();

                                links.Add(link);
                            }
                        }
                    }
                    else
                    {
                        FindRsLinksInPage(ref buffer, ref pwd, ref links);
                    }
                }
                catch (Exception)
                {
                    buffer = null;
                }

            } while (false);

            //<span style="text-decoration:underline"><span style="font-weight:bold">
            //<span style="color:red"><span style="font-size:18px; line-height:normal">PASS</span></span>
            //</span><span style="color:black"><span style="font-size:18px; line-height:normal">
            //<span style="font-weight:bold">WORD:</span></span></span></span>
            //<br />
            //<table width="90%" cellspacing="1" cellpadding="3" border="0" align="center">
            //<tr>
            //<td><span class="genmed"><strong>Code:</strong></span></td>
            //</tr>
            //<tr>
            //<td class="code">ShAaNiG</td></tr></table>
            //<br />

            if (buffer != null)
            {
                Regex passwordPattern = new Regex(">PASS<.+?<td class=\"code\">([^<]+)</td>", RegexOptions.Singleline);

                Match m = passwordPattern.Match(buffer);
                if (m.Success)
                {
                    pwd = m.Groups[1].Value;
                    AddPasswordToPasswordsFile(pwd);
                }

                if (links != null && links.Count > 0)
                {
                    UpdateResult(ctx.ResultId, links, m_subsLink, pwd, ResultState.Verified, ctx.Title, ctx.Link);
                }
                else
                {
                    UpdateResult(ctx.ResultId, links, m_subsLink, "", ResultState.VerifiedAndEmpty, ctx.Title, ctx.Link);
                }
            }
            else
            {
                UpdateResult(ctx.ResultId, null, null, "", ResultState.VerifiedAndEmpty, ctx.Title, ctx.Link);
            }

            ctx.FinishEvent.Set();
        }

        private void FindRsLinksInPage(ref string buffer, ref string pwd, ref List<string> links)
        {
            buffer = buffer.Replace("htt*", "http");
            string rsPrefix = "http&#58;//www.filesonic.com/";
            int rsLinksBufferStartPos = buffer.IndexOf(rsPrefix);
            if (rsLinksBufferStartPos == -1)
            {
                rsLinksBufferStartPos = buffer.IndexOf("http://www.filesonic.com/");
                if (rsLinksBufferStartPos == -1) // no rs links
                {
                    return;
                }
                else
                {
                    rsPrefix = "http://www.filesonic.com/";
                }
            }

            int rsLinksBufferEndPos = buffer.IndexOf("</td></tr></table>", rsLinksBufferStartPos);
            string rsLinksBuffer = buffer.Substring(rsLinksBufferStartPos,
                                                    rsLinksBufferEndPos - rsLinksBufferStartPos);
            if (rsLinksBuffer.ToLower().Contains("sample"))
            {
                rsLinksBufferStartPos = buffer.IndexOf(rsPrefix, rsLinksBufferEndPos);
                if (rsLinksBufferStartPos == -1)
                {
                    return;
                }
                rsLinksBufferEndPos = buffer.IndexOf("</td></tr></table>", rsLinksBufferStartPos);
                rsLinksBuffer = buffer.Substring(rsLinksBufferStartPos, rsLinksBufferEndPos - rsLinksBufferStartPos);
            }

            if (!rsLinksBuffer.Contains(".rar"))
            {
                return;
            }

            rsLinksBufferEndPos = rsLinksBuffer.LastIndexOf(".r") + 4;
            rsLinksBuffer = rsLinksBuffer.Substring(0, rsLinksBufferEndPos);

            string[] rsLinks = rsLinksBuffer.Split(new[] { rsPrefix },
                                                   StringSplitOptions.RemoveEmptyEntries);

            links = CleanLinks(rsLinks);
            if (links == null || links.Count == 0)
            {
                return;
            }

            // Search for password and add it to password list
            int passStartPos = buffer.IndexOf("Password:", rsLinksBufferEndPos);
            if (passStartPos > -1)
            {
                passStartPos = buffer.IndexOf("Code:", passStartPos);
                if (passStartPos > -1)
                {
                    string startCode = "<td class=\"code\">";
                    int startPos = buffer.IndexOf(startCode, passStartPos);
                    int endPos = -1;
                    if (startPos == -1)
                    {
                        startPos = buffer.IndexOf("Password:");
                        if (startPos != -1)
                        {
                            startCode = "class=\"codecontent\">";
                            startPos = buffer.IndexOf(startCode, startPos);
                            if (startPos != -1)
                            {
                                endPos = buffer.IndexOf("</div>", startPos);
                            }
                            else
                            {
                                return;
                            }
                        }
                        else
                        {
                            return;
                        }
                    }
                    else
                    {
                        endPos = buffer.IndexOf("</td></tr></table>", startPos);
                    }
                    if (endPos == -1)
                    {
                        return;
                    }
                    if ((endPos - startPos) < 100)
                    {
                        pwd = buffer.Substring(startPos + startCode.Length,
                                                      endPos - startPos - startCode.Length);
                        AddPasswordToPasswordsFile(pwd);
                    }
                }
            }
        }


        protected void SetSid()
        {
            NotificationsClient.SetStatusMessageAndProgressAmount("Logging in to " + SiteName, 0);

            AsyncWebRequest request = new AsyncWebRequest();
            request.SendWebBBRequest(
                             LoginURL,
                             "",
                             "username=maverick2830&password=maverick&redirect=index.php&login=Login",
                             //"username=maverick2830&password=maverick&redirect=&login=Log+in",
                             10,
                             Sid,
                             IsPost,
                             IsPost ? LoginURL : SearchURL,
                             ShouldStopEvent,
                             0,
                             "",
                             false,
                             delegate(string buffer, HttpWebResponse responseObj, string searchCoookieParam, object userState)
                             {
                                 if (string.IsNullOrEmpty(buffer))
                                 {
                                     NotificationsClient.SetStatusMessageAndProgressAmount("Failed to login to " + SiteName, 0);
                                 }
                                 else
                                 {
                                     SearchCookieParam = searchCoookieParam;

                                     const string sidHeader = "you were previously<br /><br />Click <a href=\"index.php?sid=";
                                     const string sidHeader2 = "You have sucessfully been logged into this forum<br /><br />Click <a href=\"index.php?sid=";
                                     const string sidHeader3 = "You have been successfully logged in.<br /><br /><a href=\"http://www.projectw.org/index.php?sid=";
                                     int pos1 = buffer.IndexOf(sidHeader);
                                     if (pos1 == -1)
                                     {
                                         pos1 = buffer.IndexOf(sidHeader2);
                                         if (pos1 != -1)
                                         {
                                             pos1 += sidHeader2.Length;
                                         }
                                         else
                                         {
                                             pos1 = buffer.IndexOf(sidHeader3);
                                             if (pos1 != -1)
                                             {
                                                 pos1 += sidHeader3.Length;
                                             }
                                         }
                                     }
                                     else
                                     {
                                         pos1 += sidHeader.Length;
                                     }
                                     if (pos1 != -1)
                                     {
                                         int pos2 = buffer.Substring(pos1).IndexOf("\">");
                                         if (pos2 != -1)
                                         {
                                             Sid = buffer.Substring(pos1, pos2);
                                             NotificationsClient.SetStatusMessageAndProgressAmount("Logged on successfully to " + SiteName, 0);
                                             loginSuccessfullEvent.Set();
                                         }
                                     }
                                 }

                             }, null);

            return;
        }


    }

    public interface INotifications
    {
        void SetStatusMessageAndProgressAmount(string message, int progressAmount);
    }
}